
/**
  ******************************************************************************
  * Copyright 2021 The grapilot Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       drv_udp_vcom.cpp
  * @author     baiyang
  * @date       2021-7-20
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "drv_udp_vcom2.h"
#include "gp_socket_udp.h"

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <rtdevice.h>

#include <common/console/console.h>
/*-----------------------------------macro------------------------------------*/
#define UDP_VCOM2_TASK_THREAD_PRIORITY     25
#define EVENT_UDP_VCOM2_UPDATE             (1 << 0)
/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
static struct rt_device udp_vcom2;
static gp_socket_udp udp_vcom2_obj;

struct rt_ringbuffer udp_vcom2_ringbuffer;
static uint8_t udp_vcom2_pool[2048];

bool udp_vcom2_thread_should_exit = false;

static struct rt_timer udp_vcom2_timer;
static struct rt_event udp_vcom2_event;

// UDP通信用线程
static char thread_udp_vcom2_task_stack[1024*5];
struct rt_thread thread_udp_vcom2_task_handle;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void udp_vcom2_watch_task(void *parameter);

static void udp_vcom2_timer_update(void* parameter)
{
    rt_event_send(&udp_vcom2_event, EVENT_UDP_VCOM2_UPDATE);
}

/**
  * @brief       
  * @param[in]   dev  
  * @param[out]  
  * @retval      
  * @note        
  */
static rt_err_t _init(rt_device_t dev)
{
    rt_size_t ret = 0;
    bool res = true;

    rt_ringbuffer_init(&udp_vcom2_ringbuffer,udp_vcom2_pool,sizeof(udp_vcom2_pool));

    const char* local_ip = "127.0.0.1"; //本机通信

    uint32_t mUdpPort = 27650;
    uint32_t mDesUdpPort = 27750;

    if (socket_udp_init(&udp_vcom2_obj, local_ip, mUdpPort, mDesUdpPort, local_ip, (uint32_t)20050)) {
        udp_vcom2_thread_should_exit = false;
    } else {
        res = false;
        rt_kprintf("udp_vcom2 fail\n");
    }
    
    return ret;
}


static rt_err_t _open(rt_device_t dev, rt_uint16_t oflag)
{
    rt_size_t ret = 0;
    
    return ret;
}


static rt_err_t _close(rt_device_t dev)
{
    rt_size_t ret = 0;

    udp_vcom2_thread_should_exit = true;

    return ret;
}


static rt_size_t _read(struct rt_device* dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    rt_size_t ret = 0;

    ret = rt_ringbuffer_get(&udp_vcom2_ringbuffer, (uint8_t *)buffer, size);

    return ret;
}

static rt_size_t _write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_size_t ret = 0;

    if (!udp_vcom2_thread_should_exit && socket_udp_is_connect(&udp_vcom2_obj)) {
        ret = socket_udp_send(&udp_vcom2_obj, (char*) buffer, size);
    }

    if (dev->tx_complete && ret == size) {
        dev->tx_complete(dev, (void *)buffer);
    }

    return ret;
}


static rt_err_t _control(struct rt_device* dev, int cmd, void* args)
{

    return RT_EOK;
}


void udp_vcom2_watch_task(void *parameter)
{
    char udp_data[1024] = {0};   //接收数据
    rt_err_t res;
    rt_uint32_t recv_set = 0;
    rt_uint32_t wait_set = EVENT_UDP_VCOM2_UPDATE;

    while (1) {
        /* wait event occur */
        res = rt_event_recv(&udp_vcom2_event, wait_set,
            RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
            RT_WAITING_FOREVER, &recv_set);

        if (res == RT_EOK) {
            if (recv_set & EVENT_UDP_VCOM2_UPDATE) {
                if (udp_vcom2_thread_should_exit && socket_udp_is_connect(&udp_vcom2_obj)) {
                    socket_udp_close(&udp_vcom2_obj);
                    continue;
                }

                // 如果UDP没有打开，跳过读取数据
                if (!socket_udp_is_connect(&udp_vcom2_obj))
                {
                    continue;
                }

                int num_recv = socket_udp_recv(&udp_vcom2_obj, (char* )udp_data, 1024);
                if (0 < num_recv)
                {
                    for (int i = 0; i < num_recv; i++) {
                        rt_ringbuffer_put_force(&udp_vcom2_ringbuffer, \
                            (const uint8_t*)udp_data, num_recv);
                    }

                    if (udp_vcom2.rx_indicate) {
                        udp_vcom2.rx_indicate(&udp_vcom2, num_recv);
                    }
                }
            }
        } else {
            // some err happen
            console_printf("sitl copter loop, err:%d\r\n", res);
        }
    }
}

long gp_udp_vcom2_register()
{
    rt_err_t res;

    rt_uint32_t flag = RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_TX \
                       | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_DMA_RX \
                       | RT_DEVICE_FLAG_INT_RX;

    struct rt_device* device;
    device = &udp_vcom2;

    RT_ASSERT(device != RT_NULL);
    
    device->type        = RT_Device_Class_Char; //是否修改为这个？
    device->ref_count   = 0;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    device->init        = _init;
    device->open        = _open;
    device->close       = _close;
    device->read        = _read;
    device->write       = _write;
    device->control     = _control;

    /* create event */
    rt_event_init(&udp_vcom2_event, "uvcom2", RT_IPC_FLAG_FIFO);

    /* register timer event */
    rt_timer_init(&udp_vcom2_timer, "uvcom2", udp_vcom2_timer_update, RT_NULL, 10,
        RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_HARD_TIMER);
    rt_timer_start(&udp_vcom2_timer);

    res = rt_thread_init(&thread_udp_vcom2_task_handle,
                       "uvcom2",
                       udp_vcom2_watch_task,
                       RT_NULL,
                       &thread_udp_vcom2_task_stack[0],
                       sizeof(thread_udp_vcom2_task_stack),UDP_VCOM2_TASK_THREAD_PRIORITY,5);

    if (res == RT_EOK) {
        res = rt_thread_startup(&thread_udp_vcom2_task_handle);
    }

    return rt_device_register(device, "uart2", flag);
}

/*------------------------------------test------------------------------------*/


